<?php

namespace common\db;

use Yii;
use yii\db\Expression;
use yii\helpers\ArrayHelper;
use common\helpers\ConnerTestHelper;
use Exception;

/**
 * ActiveQuery represents a DB query associated with an Active Record class.
 *
 * @author emhome<emhome@163.com>
 * @since 2.0
 */
class ActiveQuery extends \yii\db\ActiveQuery {

    use QueryDistanceTrait;

    const DEBUG_NOTES_CHAR = '    ---';

    /**
     * Sets the active part of the query.
     * @return $this the query object itself
     */
    public function active($state = false) {
        /* @var $modelClass ActiveRecord */
        $modelClass = $this->modelClass;
        $model = $modelClass::instance();
        if (!empty($model->activeAttributes)) {
            $alis = key($this->getTablesUsedInFrom()) . '.';
            $deletedAtAttribute = ArrayHelper::getValue($model->activeAttributes, 'deletedAtAttribute');
            if ($deletedAtAttribute && $model->hasAttribute($deletedAtAttribute)) {
                $this->andWhere([$alis . $deletedAtAttribute => 0]);
            }
            $activeStatusAttribute = ArrayHelper::getValue($model->activeAttributes, 'activeStatusAttribute');
            if ($state !== false && !empty($activeStatusAttribute)) {
                $activeStatusAttribute = (array) $activeStatusAttribute;
                $condition = [];
                foreach ($activeStatusAttribute as $attribute) {
                    if (!$model->hasAttribute($attribute)) {
                        continue;
                    }
                    $condition[] = [$alis . $attribute => $state];
                }
                if (!empty($condition)) {
                    array_unshift($condition, 'and');
                    $this->andWhere($condition);
                }
            }
        }
        return $this;
    }

    /**
     * Sets the active part of the query.
     * @return $this the query object itself
     */
    public function andFilterDuration($field, $begintime = null, $endtime = null) {
        $alis = key($this->getTablesUsedInFrom()) . '.';
        if ($begintime && $endtime) {
            $this->andFilterWhere(['between', $alis . $field, $begintime, $endtime]);
        } elseif ($begintime) {
            $this->andFilterWhere(['>=', $alis . $field, $begintime]);
        } elseif ($endtime) {
            $this->andFilterWhere(['<=', $alis . $field, $endtime]);
        }
        return $this;
    }

    /**
     * Sets the active part of the query.
     * @return $this the query object itself
     */
    public function andDuration($field, $begintime = null, $endtime = null) {
        $alis = key($this->getTablesUsedInFrom()) . '.';
        if ($begintime && $endtime) {
            $this->andWhere(['between', $alis . $field, $begintime, $endtime]);
        } elseif ($begintime) {
            $this->andWhere(['>=', $alis . $field, $begintime]);
        } elseif ($endtime) {
            $this->andWhere(['<=', $alis . $field, $endtime]);
        }
        return $this;
    }

    /**
     * Sets the active part of the query.
     * @return $this the query object itself
     */
    public function andCompareColumns($primaryColumns, $withColumns, $defaultOperator = '=') {
        $tableAlias = key($this->getTablesUsedInFrom()) . '.';
        if (is_string($primaryColumns) && is_string($withColumns)) {
            if (strpos($primaryColumns, '.') === false) {
                $primaryColumns = $tableAlias . $primaryColumns;
            }
            if (strpos($withColumns, '.') === false) {
                $withColumns = $tableAlias . $withColumns;
            }
            $ex = new Expression($primaryColumns . $defaultOperator . $withColumns);
            return $this->andWhere($ex);
        }
        $conditions = [];
        if (is_array($primaryColumns) && is_string($withColumns)) {
            if (strpos($withColumns, '.') === false) {
                $withColumns = $tableAlias . $withColumns;
            }
            foreach ($primaryColumns as $primary) {
                if (strpos($primary, '.') === false) {
                    $primary = $tableAlias . $primary;
                }
                $conditions[] = new Expression($primary . $defaultOperator . $withColumns);
            }
        } elseif (is_string($primaryColumns) && is_array($withColumns)) {
            if (strpos($primaryColumns, '.') === false) {
                $primaryColumns = $tableAlias . $primaryColumns;
            }
            foreach ($withColumns as $with) {
                if (strpos($with, '.') === false) {
                    $with = $tableAlias . $with;
                }
                $conditions[] = new Expression($primaryColumns . $defaultOperator . $with);
            }
        }
        if (!empty($conditions)) {
            array_unshift($conditions, 'OR');
            return $this->andFilterWhere($conditions);
        }
        return $this;
    }

    /**
     * Sets the active part of the query.
     * @return $this the query object itself
     */
    public function debug($return = false) {
        $sql = $this->createCommand()->rawSql;
        if ($return) {
            return $sql;
        }
        Yii::error(implode("\n", [
            '',
            static::DEBUG_NOTES_CHAR,
            static::DEBUG_NOTES_CHAR . '  ' . $sql,
            static::DEBUG_NOTES_CHAR,
        ]), get_called_class());
        return $this;
    }

    /**
     * Sets the active part of the query.
     * @return $this the query object itself
     */
    public function bindDebuger() {
        try {
            if (ConnerTestHelper::isInner()) {
                return $this->debug();
            }
        } catch (Exception $exc) {
            Yii::error($exc->getTraceAsString(), __METHOD__);
        }
        return $this;
    }

}
